/***************************************************************************/
/*                                                                         */
/*  pfrload.c                                                              */
/*                                                                         */
/*    FreeType PFR loader (body).                                          */
/*                                                                         */
/*  Copyright 2002, 2003, 2004, 2005, 2007, 2009 by                        */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include "pfrload.h"
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H

#include "pfrerror.h"

#undef  FT_COMPONENT
#define FT_COMPONENT trace_pfr


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                          EXTRA ITEMS                          *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/


FT_LOCAL_DEF(FT_Error)
pfr_extra_items_skip(FT_Byte * *pp,
                     FT_Byte * limit)
{
    return pfr_extra_items_parse(pp, limit, NULL, NULL);
}


FT_LOCAL_DEF(FT_Error)
pfr_extra_items_parse(FT_Byte * *pp,
                      FT_Byte * limit,
                      PFR_ExtraItem item_list,
                      FT_Pointer item_data)
{
    FT_Error error = 0;
    FT_Byte* p = *pp;
    FT_UInt num_items, item_type, item_size;


    PFR_CHECK(1);
    num_items = PFR_NEXT_BYTE(p);

    for (; num_items > 0; num_items--)
    {
        PFR_CHECK(2);
        item_size = PFR_NEXT_BYTE(p);
        item_type = PFR_NEXT_BYTE(p);

        PFR_CHECK(item_size);

        if (item_list)
        {
            PFR_ExtraItem extra = item_list;


            for (extra = item_list; extra->parser != NULL; extra++)
            {
                if (extra->type == item_type)
                {
                    error = extra->parser(p, p + item_size, item_data);
                    if (error) goto Exit;

                    break;
                }
            }
        }

        p += item_size;
    }

Exit:
    *pp = p;
    return error;

Too_Short:
    FT_ERROR(("pfr_extra_items_parse: invalid extra items table\n"));
    error = PFR_Err_Invalid_Table;
    goto Exit;
}


/*************************************************************************/
/*************************************************************************/
/*****                                                               *****/
/*****                          PFR HEADER                           *****/
/*****                                                               *****/
/*************************************************************************/
/*************************************************************************/

static const FT_Frame_Field pfr_header_fields[] =
{
    #undef  FT_STRUCTURE
#define FT_STRUCTURE PFR_HeaderRec

    FT_FRAME_START(58),
    FT_FRAME_ULONG(signature),
    FT_FRAME_USHORT(version),
    FT_FRAME_USHORT(signature2),
    FT_FRAME_USHORT(header_size),

    FT_FRAME_USHORT(log_dir_size),
    FT_FRAME_USHORT(log_dir_offset),

    FT_FRAME_USHORT(log_font_max_size),
    FT_FRAME_UOFF3(log_font_section_size),
    FT_FRAME_UOFF3(log_font_section_offset),

    FT_FRAME_USHORT(phy_font_max_size),
    FT_FRAME_UOFF3(phy_font_section_size),
    FT_FRAME_UOFF3(phy_font_section_offset),

    FT_FRAME_USHORT(gps_max_size),
    FT_FRAME_UOFF3(gps_section_size),
    FT_FRAME_UOFF3(gps_section_offset),

    FT_FRAME_BYTE(max_blue_values),
    FT_FRAME_BYTE(max_x_orus),
    FT_FRAME_BYTE(max_y_orus),

    FT_FRAME_BYTE(phy_font_max_size_high),
    FT_FRAME_BYTE(color_flags),

    FT_FRAME_UOFF3(bct_max_size),
    FT_FRAME_UOFF3(bct_set_max_size),
    FT_FRAME_UOFF3(phy_bct_set_max_size),

    FT_FRAME_USHORT(num_phy_fonts),
    FT_FRAME_BYTE(max_vert_stem_snap),
    FT_FRAME_BYTE(max_horz_stem_snap),
    FT_FRAME_USHORT(max_chars),
    FT_FRAME_END
};


FT_LOCAL_DEF(FT_Error)
pfr_header_load(PFR_Header header,
                FT_Stream stream)
{
    FT_Error error;


    /* read header directly */
    if (!FT_STREAM_SEEK(0) &&
        !FT_STREAM_READ_FIELDS(pfr_header_fields, header))
    {
        /* make a few adjustments to the header */
        header->phy_font_max_size +=
            (FT_UInt32)header->phy_font_max_size_high << 16;
    }

    return error;
}


FT_LOCAL_DEF(FT_Bool)
pfr_header_check(PFR_Header header)
{
    FT_Bool result = 1;


    /* check signature and header size */
    if (header->signature != 0x50465230L ||     /* "PFR0" */
        header->version > 4 ||
        header->header_size < 58 ||
        header->signature2 != 0x0d0a)           /* CR/LF  */
    {
        result = 0;
    }
    return result;
}


/***********************************************************************/
/***********************************************************************/
/*****                                                             *****/
/*****                    PFR LOGICAL FONTS                        *****/
/*****                                                             *****/
/***********************************************************************/
/***********************************************************************/


FT_LOCAL_DEF(FT_Error)
pfr_log_font_count(FT_Stream stream,
                   FT_UInt32 section_offset,
                   FT_UInt * acount)
{
    FT_Error error;
    FT_UInt count;
    FT_UInt result = 0;


    if (FT_STREAM_SEEK(section_offset) || FT_READ_USHORT(count))
        goto Exit;

    result = count;

Exit:
    *acount = result;
    return error;
}


FT_LOCAL_DEF(FT_Error)
pfr_log_font_load(PFR_LogFont log_font,
                  FT_Stream stream,
                  FT_UInt idx,
                  FT_UInt32 section_offset,
                  FT_Bool size_increment)
{
    FT_UInt num_log_fonts;
    FT_UInt flags;
    FT_UInt32 offset;
    FT_UInt32 size;
    FT_Error error;


    if (FT_STREAM_SEEK(section_offset) ||
        FT_READ_USHORT(num_log_fonts))
        goto Exit;

    if (idx >= num_log_fonts)
        return PFR_Err_Invalid_Argument;

    if (FT_STREAM_SKIP(idx * 5) ||
        FT_READ_USHORT(size) ||
        FT_READ_UOFF3(offset))
        goto Exit;

    /* save logical font size and offset */
    log_font->size = size;
    log_font->offset = offset;

    /* now, check the rest of the table before loading it */
    {
        FT_Byte* p;
        FT_Byte* limit;
        FT_UInt local;


        if (FT_STREAM_SEEK(offset) || FT_FRAME_ENTER(size))
            goto Exit;

        p = stream->cursor;
        limit = p + size;

        PFR_CHECK(13);

        log_font->matrix[0] = PFR_NEXT_LONG(p);
        log_font->matrix[1] = PFR_NEXT_LONG(p);
        log_font->matrix[2] = PFR_NEXT_LONG(p);
        log_font->matrix[3] = PFR_NEXT_LONG(p);

        flags = PFR_NEXT_BYTE(p);

        local = 0;
        if (flags & PFR_LOG_STROKE)
        {
            local++;
            if (flags & PFR_LOG_2BYTE_STROKE)
                local++;

            if ((flags & PFR_LINE_JOIN_MASK) == PFR_LINE_JOIN_MITER)
                local += 3;
        }
        if (flags & PFR_LOG_BOLD)
        {
            local++;
            if (flags & PFR_LOG_2BYTE_BOLD)
                local++;
        }

        PFR_CHECK(local);

        if (flags & PFR_LOG_STROKE)
        {
            log_font->stroke_thickness = (flags & PFR_LOG_2BYTE_STROKE)
                                         ? PFR_NEXT_SHORT(p)
                                         : PFR_NEXT_BYTE(p);

            if ((flags & PFR_LINE_JOIN_MASK) == PFR_LINE_JOIN_MITER)
                log_font->miter_limit = PFR_NEXT_LONG(p);
        }

        if (flags & PFR_LOG_BOLD)
        {
            log_font->bold_thickness = (flags & PFR_LOG_2BYTE_BOLD)
                                       ? PFR_NEXT_SHORT(p)
                                       : PFR_NEXT_BYTE(p);
        }

        if (flags & PFR_LOG_EXTRA_ITEMS)
        {
            error = pfr_extra_items_skip(&p, limit);
            if (error) goto Fail;
        }

        PFR_CHECK(5);
        log_font->phys_size = PFR_NEXT_USHORT(p);
        log_font->phys_offset = PFR_NEXT_ULONG(p);
        if (size_increment)
        {
            PFR_CHECK(1);
            log_font->phys_size += (FT_UInt32)PFR_NEXT_BYTE(p) << 16;
        }
    }

Fail:
    FT_FRAME_EXIT();

Exit:
    return error;

Too_Short:
    FT_ERROR(("pfr_log_font_load: invalid logical font table\n"));
    error = PFR_Err_Invalid_Table;
    goto Fail;
}


/***********************************************************************/
/***********************************************************************/
/*****                                                             *****/
/*****                    PFR PHYSICAL FONTS                       *****/
/*****                                                             *****/
/***********************************************************************/
/***********************************************************************/


/* load bitmap strikes lists */
FT_CALLBACK_DEF(FT_Error)
pfr_extra_item_load_bitmap_info(FT_Byte * p,
                                FT_Byte * limit,
                                PFR_PhyFont phy_font)
{
    FT_Memory memory = phy_font->memory;
    PFR_Strike strike;
    FT_UInt flags0;
    FT_UInt n, count, size1;
    FT_Error error = 0;


    PFR_CHECK(5);

    p += 3;  /* skip bctSize */
    flags0 = PFR_NEXT_BYTE(p);
    count = PFR_NEXT_BYTE(p);

    /* re-allocate when needed */
    if (phy_font->num_strikes + count > phy_font->max_strikes)
    {
        FT_UInt new_max = FT_PAD_CEIL(phy_font->num_strikes + count, 4);


        if (FT_RENEW_ARRAY(phy_font->strikes,
                           phy_font->num_strikes,
                           new_max))
            goto Exit;

        phy_font->max_strikes = new_max;
    }

    size1 = 1 + 1 + 1 + 2 + 2 + 1;
    if (flags0 & PFR_STRIKE_2BYTE_XPPM)
        size1++;

    if (flags0 & PFR_STRIKE_2BYTE_YPPM)
        size1++;

    if (flags0 & PFR_STRIKE_3BYTE_SIZE)
        size1++;

    if (flags0 & PFR_STRIKE_3BYTE_OFFSET)
        size1++;

    if (flags0 & PFR_STRIKE_2BYTE_COUNT)
        size1++;

    strike = phy_font->strikes + phy_font->num_strikes;

    PFR_CHECK(count * size1);

    for (n = 0; n < count; n++, strike++)
    {
        strike->x_ppm = (flags0 & PFR_STRIKE_2BYTE_XPPM)
                        ? PFR_NEXT_USHORT(p)
                        : PFR_NEXT_BYTE(p);

        strike->y_ppm = (flags0 & PFR_STRIKE_2BYTE_YPPM)
                        ? PFR_NEXT_USHORT(p)
                        : PFR_NEXT_BYTE(p);

        strike->flags = PFR_NEXT_BYTE(p);

        strike->bct_size = (flags0 & PFR_STRIKE_3BYTE_SIZE)
                           ? PFR_NEXT_ULONG(p)
                           : PFR_NEXT_USHORT(p);

        strike->bct_offset = (flags0 & PFR_STRIKE_3BYTE_OFFSET)
                             ? PFR_NEXT_ULONG(p)
                             : PFR_NEXT_USHORT(p);

        strike->num_bitmaps = (flags0 & PFR_STRIKE_2BYTE_COUNT)
                              ? PFR_NEXT_USHORT(p)
                              : PFR_NEXT_BYTE(p);
    }

    phy_font->num_strikes += count;

Exit:
    return error;

Too_Short:
    error = PFR_Err_Invalid_Table;
    FT_ERROR(("pfr_extra_item_load_bitmap_info:"
              " invalid bitmap info table\n"));
    goto Exit;
}


/* Load font ID.  This is a so-called "unique" name that is rather
 * long and descriptive (like "Tiresias ScreenFont v7.51").
 *
 * Note that a PFR font's family name is contained in an *undocumented*
 * string of the "auxiliary data" portion of a physical font record.  This
 * may also contain the "real" style name!
 *
 * If no family name is present, the font ID is used instead for the
 * family.
 */
FT_CALLBACK_DEF(FT_Error)
pfr_extra_item_load_font_id(FT_Byte * p,
                            FT_Byte * limit,
                            PFR_PhyFont phy_font)
{
    FT_Error error = 0;
    FT_Memory memory = phy_font->memory;
    FT_PtrDist len = limit - p;


    if (phy_font->font_id != NULL)
        goto Exit;

    if (FT_ALLOC(phy_font->font_id, len + 1))
        goto Exit;

    /* copy font ID name, and terminate it for safety */
    FT_MEM_COPY(phy_font->font_id, p, len);
    phy_font->font_id[len] = 0;

Exit:
    return error;
}


/* load stem snap tables */
FT_CALLBACK_DEF(FT_Error)
pfr_extra_item_load_stem_snaps(FT_Byte * p,
                               FT_Byte * limit,
                               PFR_PhyFont phy_font)
{
    FT_UInt count, num_vert, num_horz;
    FT_Int* snaps;
    FT_Error error = 0;
    FT_Memory memory = phy_font->memory;


    if (phy_font->vertical.stem_snaps != NULL)
        goto Exit;

    PFR_CHECK(1);
    count = PFR_NEXT_BYTE(p);

    num_vert = count & 15;
    num_horz = count >> 4;
    count = num_vert + num_horz;

    PFR_CHECK(count * 2);

    if (FT_NEW_ARRAY(snaps, count))
        goto Exit;

    phy_font->vertical.stem_snaps = snaps;
    phy_font->horizontal.stem_snaps = snaps + num_vert;

    for (; count > 0; count--, snaps++)
        *snaps = FT_NEXT_SHORT(p);

Exit:
    return error;

Too_Short:
    error = PFR_Err_Invalid_Table;
    FT_ERROR(("pfr_exta_item_load_stem_snaps:"
              " invalid stem snaps table\n"));
    goto Exit;
}



/* load kerning pair data */
FT_CALLBACK_DEF(FT_Error)
pfr_extra_item_load_kerning_pairs(FT_Byte * p,
                                  FT_Byte * limit,
                                  PFR_PhyFont phy_font)
{
    PFR_KernItem item;
    FT_Error error = 0;
    FT_Memory memory = phy_font->memory;


    FT_TRACE2(("pfr_extra_item_load_kerning_pairs()\n"));

    if (FT_NEW(item))
        goto Exit;

    PFR_CHECK(4);

    item->pair_count = PFR_NEXT_BYTE(p);
    item->base_adj = PFR_NEXT_SHORT(p);
    item->flags = PFR_NEXT_BYTE(p);
    item->offset = phy_font->offset + (p - phy_font->cursor);

    #ifndef PFR_CONFIG_NO_CHECKS
    item->pair_size = 3;

    if (item->flags & PFR_KERN_2BYTE_CHAR)
        item->pair_size += 2;

    if (item->flags & PFR_KERN_2BYTE_ADJ)
        item->pair_size += 1;

    PFR_CHECK(item->pair_count * item->pair_size);
    #endif

    /* load first and last pairs into the item to speed up */
    /* lookup later...                                     */
    if (item->pair_count > 0)
    {
        FT_UInt char1, char2;
        FT_Byte* q;


        if (item->flags & PFR_KERN_2BYTE_CHAR)
        {
            q = p;
            char1 = PFR_NEXT_USHORT(q);
            char2 = PFR_NEXT_USHORT(q);

            item->pair1 = PFR_KERN_INDEX(char1, char2);

            q = p + item->pair_size * (item->pair_count - 1);
            char1 = PFR_NEXT_USHORT(q);
            char2 = PFR_NEXT_USHORT(q);

            item->pair2 = PFR_KERN_INDEX(char1, char2);
        }
        else
        {
            q = p;
            char1 = PFR_NEXT_BYTE(q);
            char2 = PFR_NEXT_BYTE(q);

            item->pair1 = PFR_KERN_INDEX(char1, char2);

            q = p + item->pair_size * (item->pair_count - 1);
            char1 = PFR_NEXT_BYTE(q);
            char2 = PFR_NEXT_BYTE(q);

            item->pair2 = PFR_KERN_INDEX(char1, char2);
        }

        /* add new item to the current list */
        item->next = NULL;
        *phy_font->kern_items_tail = item;
        phy_font->kern_items_tail = &item->next;
        phy_font->num_kern_pairs += item->pair_count;
    }
    else
    {
        /* empty item! */
        FT_FREE(item);
    }

Exit:
    return error;

Too_Short:
    FT_FREE(item);

    error = PFR_Err_Invalid_Table;
    FT_ERROR(("pfr_extra_item_load_kerning_pairs:"
              " invalid kerning pairs table\n"));
    goto Exit;
}



static const PFR_ExtraItemRec pfr_phy_font_extra_items[] =
{
    { 1, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_bitmap_info },
    { 2, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_font_id },
    { 3, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_stem_snaps },
    { 4, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_kerning_pairs },
    { 0, NULL }
};


/* Loads a name from the auxiliary data.  Since this extracts undocumented
 * strings from the font file, we need to be careful here.
 */
static FT_Error
pfr_aux_name_load(FT_Byte* p,
                  FT_UInt len,
                  FT_Memory memory,
                  FT_String** astring)
{
    FT_Error error = 0;
    FT_String* result = NULL;
    FT_UInt n, ok;


    if (len > 0 && p[len - 1] == 0)
        len--;

    /* check that each character is ASCII for making sure not to
       load garbage
     */
    ok = (len > 0);
    for (n = 0; n < len; n++)
        if (p[n] < 32 || p[n] > 127)
        {
            ok = 0;
            break;
        }

    if (ok)
    {
        if (FT_ALLOC(result, len + 1))
            goto Exit;

        FT_MEM_COPY(result, p, len);
        result[len] = 0;
    }
Exit:
    *astring = result;
    return error;
}


FT_LOCAL_DEF(void)
pfr_phy_font_done(PFR_PhyFont phy_font,
                  FT_Memory memory)
{
    FT_FREE(phy_font->font_id);
    FT_FREE(phy_font->family_name);
    FT_FREE(phy_font->style_name);

    FT_FREE(phy_font->vertical.stem_snaps);
    phy_font->vertical.num_stem_snaps = 0;

    phy_font->horizontal.stem_snaps = NULL;
    phy_font->horizontal.num_stem_snaps = 0;

    FT_FREE(phy_font->strikes);
    phy_font->num_strikes = 0;
    phy_font->max_strikes = 0;

    FT_FREE(phy_font->chars);
    phy_font->num_chars = 0;
    phy_font->chars_offset = 0;

    FT_FREE(phy_font->blue_values);
    phy_font->num_blue_values = 0;

    {
        PFR_KernItem item, next;


        item = phy_font->kern_items;
        while (item)
        {
            next = item->next;
            FT_FREE(item);
            item = next;
        }
        phy_font->kern_items = NULL;
        phy_font->kern_items_tail = NULL;
    }

    phy_font->num_kern_pairs = 0;
}


FT_LOCAL_DEF(FT_Error)
pfr_phy_font_load(PFR_PhyFont phy_font,
                  FT_Stream stream,
                  FT_UInt32 offset,
                  FT_UInt32 size)
{
    FT_Error error;
    FT_Memory memory = stream->memory;
    FT_UInt flags;
    FT_ULong num_aux;
    FT_Byte* p;
    FT_Byte* limit;


    phy_font->memory = memory;
    phy_font->offset = offset;

    phy_font->kern_items = NULL;
    phy_font->kern_items_tail = &phy_font->kern_items;

    if (FT_STREAM_SEEK(offset) || FT_FRAME_ENTER(size))
        goto Exit;

    phy_font->cursor = stream->cursor;

    p = stream->cursor;
    limit = p + size;

    PFR_CHECK(15);
    phy_font->font_ref_number = PFR_NEXT_USHORT(p);
    phy_font->outline_resolution = PFR_NEXT_USHORT(p);
    phy_font->metrics_resolution = PFR_NEXT_USHORT(p);
    phy_font->bbox.xMin = PFR_NEXT_SHORT(p);
    phy_font->bbox.yMin = PFR_NEXT_SHORT(p);
    phy_font->bbox.xMax = PFR_NEXT_SHORT(p);
    phy_font->bbox.yMax = PFR_NEXT_SHORT(p);
    phy_font->flags = flags = PFR_NEXT_BYTE(p);

    /* get the standard advance for non-proportional fonts */
    if (!(flags & PFR_PHY_PROPORTIONAL))
    {
        PFR_CHECK(2);
        phy_font->standard_advance = PFR_NEXT_SHORT(p);
    }

    /* load the extra items when present */
    if (flags & PFR_PHY_EXTRA_ITEMS)
    {
        error = pfr_extra_items_parse(&p, limit,
                                      pfr_phy_font_extra_items, phy_font);

        if (error)
            goto Fail;
    }

    /* In certain fonts, the auxiliary bytes contain interesting  */
    /* information. These are not in the specification but can be */
    /* guessed by looking at the content of a few PFR0 fonts.     */
    PFR_CHECK(3);
    num_aux = PFR_NEXT_ULONG(p);

    if (num_aux > 0)
    {
        FT_Byte* q = p;
        FT_Byte* q2;


        PFR_CHECK(num_aux);
        p += num_aux;

        while (num_aux > 0)
        {
            FT_UInt length, type;


            if (q + 4 > p)
                break;

            length = PFR_NEXT_USHORT(q);
            if (length < 4 || length > num_aux)
                break;

            q2 = q + length - 2;
            type = PFR_NEXT_USHORT(q);

            switch (type)
            {
                case 1:
                    /* this seems to correspond to the font's family name,
                     * padded to 16-bits with one zero when necessary
                     */
                    error = pfr_aux_name_load(q, length - 4U, memory,
                                              &phy_font->family_name);
                    if (error)
                        goto Exit;
                    break;

                case 2:
                    if (q + 32 > q2)
                        break;

                    q += 10;
                    phy_font->ascent = PFR_NEXT_SHORT(q);
                    phy_font->descent = PFR_NEXT_SHORT(q);
                    phy_font->leading = PFR_NEXT_SHORT(q);
                    q += 16;
                    break;

                case 3:
                    /* this seems to correspond to the font's style name,
                     * padded to 16-bits with one zero when necessary
                     */
                    error = pfr_aux_name_load(q, length - 4U, memory,
                                              &phy_font->style_name);
                    if (error)
                        goto Exit;
                    break;

                default:
                    ;
            }

            q = q2;
            num_aux -= length;
        }
    }

    /* read the blue values */
    {
        FT_UInt n, count;


        PFR_CHECK(1);
        phy_font->num_blue_values = count = PFR_NEXT_BYTE(p);

        PFR_CHECK(count * 2);

        if (FT_NEW_ARRAY(phy_font->blue_values, count))
            goto Fail;

        for (n = 0; n < count; n++)
            phy_font->blue_values[n] = PFR_NEXT_SHORT(p);
    }

    PFR_CHECK(8);
    phy_font->blue_fuzz = PFR_NEXT_BYTE(p);
    phy_font->blue_scale = PFR_NEXT_BYTE(p);

    phy_font->vertical.standard = PFR_NEXT_USHORT(p);
    phy_font->horizontal.standard = PFR_NEXT_USHORT(p);

    /* read the character descriptors */
    {
        FT_UInt n, count, Size;


        phy_font->num_chars = count = PFR_NEXT_USHORT(p);
        phy_font->chars_offset = offset + (p - stream->cursor);

        if (FT_NEW_ARRAY(phy_font->chars, count))
            goto Fail;

        Size = 1 + 1 + 2;
        if (flags & PFR_PHY_2BYTE_CHARCODE)
            Size += 1;

        if (flags & PFR_PHY_PROPORTIONAL)
            Size += 2;

        if (flags & PFR_PHY_ASCII_CODE)
            Size += 1;

        if (flags & PFR_PHY_2BYTE_GPS_SIZE)
            Size += 1;

        if (flags & PFR_PHY_3BYTE_GPS_OFFSET)
            Size += 1;

        PFR_CHECK(count * Size);

        for (n = 0; n < count; n++)
        {
            PFR_Char cur = &phy_font->chars[n];


            cur->char_code = (flags & PFR_PHY_2BYTE_CHARCODE)
                             ? PFR_NEXT_USHORT(p)
                             : PFR_NEXT_BYTE(p);

            cur->advance = (flags & PFR_PHY_PROPORTIONAL)
                           ? PFR_NEXT_SHORT(p)
                           : (FT_Int)phy_font->standard_advance;

            #if 0
            cur->ascii = (flags & PFR_PHY_ASCII_CODE)
                         ? PFR_NEXT_BYTE(p)
                         : 0;
            #else
            if (flags & PFR_PHY_ASCII_CODE)
                p += 1;
            #endif
            cur->gps_size = (flags & PFR_PHY_2BYTE_GPS_SIZE)
                            ? PFR_NEXT_USHORT(p)
                            : PFR_NEXT_BYTE(p);

            cur->gps_offset = (flags & PFR_PHY_3BYTE_GPS_OFFSET)
                              ? PFR_NEXT_ULONG(p)
                              : PFR_NEXT_USHORT(p);
        }
    }

    /* that's it! */

Fail:
    FT_FRAME_EXIT();

    /* save position of bitmap info */
    phy_font->bct_offset = FT_STREAM_POS();
    phy_font->cursor = NULL;

Exit:
    return error;

Too_Short:
    error = PFR_Err_Invalid_Table;
    FT_ERROR(("pfr_phy_font_load: invalid physical font table\n"));
    goto Fail;
}


/* END */